package com.logicbus.dbcp.processor;

import com.anysoft.formula.DataProvider;
import com.anysoft.formula.ExprValue;
import com.anysoft.formula.Expression;
import com.anysoft.formula.FormulaException;
import com.anysoft.formula.Function;
import com.anysoft.util.KeyGen;
import org.apache.commons.lang3.StringUtils;

import java.util.regex.Pattern;

/**
 * 插件
 * 
 * @author duanyy
 * 
 * @version 1.6.5.8 [20160601 duanyy] <br>
 * - 考虑到其它数据库的需求，支持自定义绑定变量名(1.6.5.8) <br>
 *
 * @version 1.6.13.5 [20200605 duanyy] <br>
 * - 增加bind_long,bind_double,bind_auto等指定类型的绑定功能 <br>
 *
 * @version 1.6.15.4 [20211112 duanyy] <br>
 * - 增加bind_array功能 <br>
 */
abstract public class Plugin extends Function {
	protected BindedListener bindedListener = null;
	
	public Plugin(String name,BindedListener listener){
		super(name);
		bindedListener = listener;
	}
	
	public void bind(Object value){
		if (bindedListener != null){
			bindedListener.bind(value);
		}
	}
	
	/**
	 * NotNull
	 * @author duanyy
	 *
	 */
	public static class NotNull extends Plugin {

		public NotNull(String name, BindedListener listener) {
			super(name, listener);
		}

		@Override
		public void checkArgument(Expression arg) throws FormulaException {
			if (getArgumentCount() > 2){
				throw new FormulaException("not_nvl function only supports 2 arguments.");
			}	
		}

		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			if (getArgumentCount() != 2){
				throw new FormulaException("not_nvl function need 2 arguments.we have " + getArgumentCount());
			}
			ExprValue value = getArgument(0).getValue(provider);
			if (value != null){
				value = getArgument(1).getValue(provider);
			}
			if (value == null){
				value = new ExprValue("");
			}
			return value;
		}
	}
	
	/**
	 * bind插件
	 * 
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class Bind extends Plugin {
		protected Object varContext = null;
		public static final Pattern lType = Pattern.compile("^[\\+\\-]?[0-9]*$");
		public static final Pattern dType = Pattern.compile("^[\\+\\-]?[0-9]*\\.?[0-9]*$");
		public static final Pattern eType = Pattern.compile("^[\\+\\-]?[0-9]+[\\.][0-9]*?[Ee][+-]?[0-9]+?$");
		public static boolean isLong(String v){
			return lType.matcher(v).find();
		}
		public static long getLong(String v,long dft){
			try{
				return Long.parseLong(v);
			}catch (NumberFormatException ex){
				return dft;
			}
		}
		public static boolean isDouble(String v){
			return dType.matcher(v).find() || eType.matcher(v).find();
		}
		public static double getDouble(String v,double dft){
			try{
				return Double.parseDouble(v);
			}catch (NumberFormatException ex){
				return dft;
			}
		}

		public Bind(String name, BindedListener listener) {
			super(name, listener);
		}

		@Override
		public void checkArgument(Expression arg) throws FormulaException {
			if (getArgumentCount() > 2){
				throw new FormulaException("bind function only supports less than 2 arguments.");
			}	
		}

		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			if (arguCnt > 2 || arguCnt <= 0) {
				throw new FormulaException(
						"bind function need 1 argument.we have "
								+ arguCnt);
			}

			ExprValue value = getArgument(0).getValue(provider);

			String varName = value.toString();
			if (varContext == null) {
				varContext = provider.getContext(varName);
			}

			String object = provider.getValue(varName, varContext, "");
			bind(object);
			
			if (arguCnt <= 1){
				return new ExprValue("?");
			}else{
				return getArgument(1).getValue(provider);
			}
		}
	}

	/**
	 * bind插件
	 *
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class BindLong extends Bind {

		public BindLong(String name, BindedListener listener) {
			super(name, listener);
		}

		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			if (arguCnt > 2 || arguCnt <= 0) {
				throw new FormulaException(
						"bind function need 1 argument.we have "
								+ arguCnt);
			}

			ExprValue value = getArgument(0).getValue(provider);

			String varName = value.toString();
			if (varContext == null) {
				varContext = provider.getContext(varName);
			}

			String object = provider.getValue(varName, varContext, "");
			if (isLong(object)){
				long longValue = getLong(object,0);
				bind(longValue);
			}else {
				if (isDouble(object)){
					double doubleValue = getDouble(object,0.0);
					bind(Math.round(doubleValue));
				}else {
					bind(object);
				}
			}
			if (arguCnt <= 1){
				return new ExprValue("?");
			}else{
				return getArgument(1).getValue(provider);
			}
		}
	}

	/**
	 * bind插件
	 *
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class BindArray extends Bind {

		public BindArray(String name, BindedListener listener) {
			super(name, listener);
		}
		@Override
		public void checkArgument(Expression arg) throws FormulaException {
			if (getArgumentCount() > 3){
				throw new FormulaException("bind_array function only supports less than 2 arguments.");
			}
		}
		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			if (arguCnt > 3 || arguCnt <= 0) {
				throw new FormulaException(
						"bind function need 3 argument.we have "
								+ arguCnt);
			}

			ExprValue varId = getArgument(0).getValue(provider);
			String varName = varId.toString();
			if (varContext == null) {
				varContext = provider.getContext(varName);
			}
			String object = provider.getValue(varName, varContext, "");

			String delimiter = ",";
			if (arguCnt >= 2){
				delimiter = getArgument(1).getValue(provider).toString();
			}

			String placeHolder = "?";
			if (arguCnt >= 3){
				placeHolder = getArgument(2).getValue(provider).toString();
			}

			StringBuffer placeHolders = new StringBuffer();
			String[] items = object.split(delimiter);
			boolean first = true;
			for (String item:items){
				if (StringUtils.isNotEmpty(item)) {
					if (!first) {
						placeHolders.append(",");
					} else {
						first = false;
					}
					placeHolders.append(placeHolder);
					bind(item);
				}
			}

			return new ExprValue(placeHolders.toString());
		}
	}

	/**
	 * bind插件
	 *
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class BindArrayLong extends Bind {

		public BindArrayLong(String name, BindedListener listener) {
			super(name, listener);
		}
		@Override
		public void checkArgument(Expression arg) throws FormulaException {
			if (getArgumentCount() > 3){
				throw new FormulaException("bind_array_long function only supports less than 2 arguments.");
			}
		}
		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			if (arguCnt > 3 || arguCnt <= 0) {
				throw new FormulaException(
						"bind function need 3 argument.we have "
								+ arguCnt);
			}

			ExprValue varId = getArgument(0).getValue(provider);
			String varName = varId.toString();
			if (varContext == null) {
				varContext = provider.getContext(varName);
			}
			String object = provider.getValue(varName, varContext, "");

			String delimiter = ",";
			if (arguCnt >= 2){
				delimiter = getArgument(1).getValue(provider).toString();
			}

			String placeHolder = "?";
			if (arguCnt >= 3){
				placeHolder = getArgument(2).getValue(provider).toString();
			}

			StringBuffer placeHolders = new StringBuffer();
			String[] items = object.split(delimiter);
			boolean first = true;
			for (String item:items){
				if (StringUtils.isNotEmpty(item)) {
					if (!first) {
						placeHolders.append(",");
					} else {
						first = false;
					}
					placeHolders.append(placeHolder);
					if (isLong(item)) {
						long longValue = getLong(item, 0);
						bind(longValue);
					} else {
						if (isDouble(item)) {
							double doubleValue = getDouble(item, 0.0);
							bind(Math.round(doubleValue));
						} else {
							bind(item);
						}
					}
				}
			}

			return new ExprValue(placeHolders.toString());
		}
	}

	/**
	 * bind插件
	 *
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class BindDouble extends Bind {

		public BindDouble(String name, BindedListener listener) {
			super(name, listener);
		}

		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			if (arguCnt > 2 || arguCnt <= 0) {
				throw new FormulaException(
						"bind function need 1 argument.we have "
								+ arguCnt);
			}

			ExprValue value = getArgument(0).getValue(provider);

			String varName = value.toString();
			if (varContext == null) {
				varContext = provider.getContext(varName);
			}

			String object = provider.getValue(varName, varContext, "");
			if (isLong(object) || isDouble(object)){
				double doubleValue = getDouble(object,0.0);
				bind(doubleValue);
			}else {
				bind(object);
			}
			if (arguCnt <= 1){
				return new ExprValue("?");
			}else{
				return getArgument(1).getValue(provider);
			}
		}
	}

	/**
	 * bind插件
	 *
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class BindAuto extends Bind {

		public BindAuto(String name, BindedListener listener) {
			super(name, listener);
		}

		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			if (arguCnt > 2 || arguCnt <= 0) {
				throw new FormulaException(
						"bind function need 1 argument.we have "
								+ arguCnt);
			}

			ExprValue value = getArgument(0).getValue(provider);

			String varName = value.toString();
			if (varContext == null) {
				varContext = provider.getContext(varName);
			}

			String object = provider.getValue(varName, varContext, "");
			if (isLong(object)){
				long longValue = getLong(object,0);
				bind(longValue);
			}else {
				if (isDouble(object)){
					double doubleValue = getDouble(object,0.0);
					bind(doubleValue);
				}else {
					bind(object);
				}
			}
			if (arguCnt <= 1){
				return new ExprValue("?");
			}else{
				return getArgument(1).getValue(provider);
			}
		}
	}
	
	/**
	 * bind插件
	 * 
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class BindRaw extends Plugin {
		protected Object varContext = null;
		
		public BindRaw(String name, BindedListener listener) {
			super(name, listener);
		}

		@Override
		public void checkArgument(Expression arg) throws FormulaException {
			if (getArgumentCount() > 2){
				throw new FormulaException("bind_raw function only supports less than 2 arguments.");
			}	
		}

		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			if (arguCnt > 2 || arguCnt <= 0) {
				throw new FormulaException(
						"bind function need 1 argument.we have "
								+ arguCnt);
			}

			ExprValue value = getArgument(0).getValue(provider);

			String varName = value.toString();
			if (varContext == null) {
				varContext = provider.getContext(varName);
			}

			String object = provider.getRawValue(varName, varContext, "");
			bind(object);
			if (arguCnt <= 1){
				return new ExprValue("?");
			}else{
				return getArgument(1).getValue(provider);
			}
		}
	}
	
	/**
	 * uuid
	 * 
	 * @author duanyy
	 * @since 1.6.3.30
	 */
	public static class UUId extends Plugin {
		protected Object varContext = null;
		
		public UUId(String name, BindedListener listener) {
			super(name, listener);
		}

		@Override
		public void checkArgument(Expression arg) throws FormulaException {

		}

		@Override
		public ExprValue getValue(DataProvider provider) throws FormulaException {
			int arguCnt = getArgumentCount();
			
			ExprValue bindValue = null;
			if (arguCnt <= 0){
				bindValue = new ExprValue("?");
			}else{
				bindValue = getArgument(0).getValue(provider);		
				if (arguCnt <= 1){
					bind(KeyGen.uuid());
				}else{
					if (arguCnt <= 2){
						int length = getArgument(1).getValue(provider).getInt();
						bind(KeyGen.uuid(length, 0));
					}else{
						int length = getArgument(1).getValue(provider).getInt();
						int redix = getArgument(2).getValue(provider).getInt();
						bind(KeyGen.uuid(length, redix));
					}					
				}
			}
			
			return bindValue;
		}
	}	
}
